#include <muduo/net/TcpClient.h>
#include <muduo/net/EventLoop.h>
#include <muduo/net/EventLoopThread.h>
#include <muduo/net/TcpConnection.h>
#include <muduo/net/Buffer.h>
#include <muduo/base/CountDownLatch.h>
#include <iostream>
#include <string>

class DictClient 
{
public:
    DictClient(const std::string &sip, int sport):
        /* muduo::net::EventLoop *muduo::net::EventLoopThread::startLoop()
        _loopthread.startLoop() 方法会启动一个新的线程，并在该线程中创建一个EventLoop对象并自动事件循环监控
        */
        _baseloop(_loopthread.startLoop()),
        /* muduo::CountDownLatch _downlatch;
        该构造函数接受一个整数参数，该参数指定了需要在CountDownLatch上等待的线程数量
        构造函数会初始化一个内部计数器，该计数器的初始值等于传入的线程数量
        当计数器值减为0时，所有在CountDownLatch上等待的线程都会被唤醒并继续执行
        */
        _downlatch(1), // 初始化计数器为1
        /* muduo::net::TcpClient _client;构造说明
        EventLoop* loop: 管理着客户端的事件循环。客户端的所有I/O事件（如连接尝试、数据接收和发送等）都将通过这个事件循环来处理
        const InetAddress& serverAddr: 绑定服务器地址，它封装了服务器的IP地址和端口号
        const string& nameArg: 客户端的名称
        */
        _client(_baseloop, muduo::net::InetAddress(sip, sport), "DictClient")
    {
        // 设置连接事件（连接建立/管理）的回调，即向里面注册
        _client.setConnectionCallback(std::bind(&DictClient::onConnection, this, std::placeholders::_1));
        // 设置连接消息的回调，即向里面注册
        _client.setMessageCallback(std::bind(&DictClient::onMessage, this, 
            std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
        // 非阻塞连接服务器，因此，为了保证连接建立完毕后才开始通信，即线程同步，这里借助_downlatch
        _client.connect();
        _downlatch.wait();  // 调用该方法的线程会被挂起，直到CountDownLatch的内部计数器值减为0
    }

    bool send(const std::string &msg)
    {
        if (_conn->connected() == false) 
        {
            std::cout << "连接已经断开，发送数据失败！\n";
            return false;
        }
        _conn->send(msg);
        return true;
    }
private:
    void onConnection(const muduo::net::TcpConnectionPtr &conn) 
    {
        if (conn->connected()) 
        {
            std::cout << "连接建立！\n";
            // 将CountDownLatch的内部计数器值减1。当减为0时，所有在CountDownLatch上等待的线程都会被唤醒
            // 每当一个线程完成了它的任务时，它会调用这个方法
            _downlatch.countDown(); 
            _conn = conn;
        }
        else 
        {
            std::cout << "连接断开！\n";
            _conn.reset(); // 清空连接
        }
    }
    void onMessage(const muduo::net::TcpConnectionPtr &conn, muduo::net::Buffer *buf, muduo::Timestamp)
    {
        std::string res = buf->retrieveAllAsString();
        std::cout << res << std::endl;
    }
private:
    // 原型：typedef std::shared_ptr<muduo::net::TcpConnection> muduo::net::TcpConnectionPtr
    muduo::net::TcpConnectionPtr _conn;
    // 控制该线程的执行顺序，保证同步工作，即先建立连接，后通信
    muduo::CountDownLatch _downlatch;
    // 想要通信，必须先开启事件循环监控，但事件循环监控是个死循环，所以这里使用多线程机制，让它在线程里面进行事件循环监控
    muduo::net::EventLoopThread _loopthread; // 一个封装了EventLoop和操作系统线程的类，它负责在线程中运行EventLoop对象，并处理各种事件回调
    muduo::net::EventLoop *_baseloop;
    muduo::net::TcpClient _client;
};

int main()
{
    DictClient client("127.0.0.1", 8888);
    while(1) 
    {
        std::string msg;
        std::cin >> msg;
        client.send(msg);
    }
    return 0;
}